﻿//-----------------------------------------------------------------------
// <copyright file="DepthFirstAgendaList.cs" company="tuliprules.org">
//     Copyright (c) 2010 tuplelister, tuliprules.org.
// </copyright>
// <summary>This is the DepthFirstAgendaList class.</summary>
//-----------------------------------------------------------------------

namespace Tulip.Engine.ConflictResolution
{
    using System.Globalization;
    using Tuple = Tuples.Tuple;

    /// <summary>
    /// Agenda list for Depth-First conflict resolution strategy.
    /// </summary>
    internal class DepthFirstAgendaList : IAgendaList
    {
        /// <summary>
        /// Head tuple in the list.
        /// </summary>
        private Tuple head;

        /// <summary>
        /// Tail tuple in the list.
        /// </summary>
        private Tuple tail;

        /// <summary>
        /// Count of tuples in the list.
        /// </summary>
        private int count;

        #region IAgendaQueue Members

        /// <summary>
        /// Gets or sets the count of tuples int he list.
        /// </summary>
        public int Count
        {
            get
            {
                return this.count;
            }

            set
            {
                this.count = value;
            }
        }

        /// <summary>
        /// Adds a production tuple to thje agenda list.
        /// </summary>
        /// <param name="production">The production tuple to be added to the agenda list.</param>
        public void Push(Tuple production)
        {
            // We make the assumption here that items are queued strictly in the order they
            // are added to the agenda.
            if (this.head == null)
            {
                this.head = this.tail = production;
            }
            else
            {
                var currentTail = this.tail;
                production.AgendaPrevious = currentTail;
                currentTail.AgendaNext = production;
                this.tail = production;
            }

            production.AgendaTimestamp = this.count;
            this.count++;

            TraceFactIds("==> Activating '" + production.ProductionName + "': ", production);
        }

        /// <summary>
        /// Removes and returns the next production tuple from the agenda list. 
        /// </summary>
        /// <returns>The next production tuple to be removed from the agenda list.</returns>
        public Tuple Pop()
        {
            Tuple returnProduction = null;

            if (this.tail == null)
            {
                TraceFactIds("FIRE '" + returnProduction.ProductionName + "': ", returnProduction);
                return returnProduction;
            }

            returnProduction = this.tail;
            this.tail.AgendaTimestamp = -1;
            this.RemoveProduction(this.tail);

            TraceFactIds("FIRE '" + returnProduction.ProductionName + "': ", returnProduction);
            return returnProduction;
        }

        /// <summary>
        /// Returns, but does not remove, the next production tuple in the agenda list.
        /// </summary>
        /// <returns>The next production tuple to be removed from the agenda list.</returns>
        public Tuple Peek()
        {
            Tuple returnProduction = null;

            if (this.tail == null)
            {
                return returnProduction;
            }

            returnProduction = this.tail;
            return returnProduction;
        }

        /// <summary>
        /// Removes a production tuple from the agenda list.
        /// </summary>
        /// <param name="production">The production tuple to be removed from the agenda list.</param>
        public void Remove(Tuple production)
        {
            this.RemoveProduction(production);
            TraceFactIds("<== Deactivating '" + production.ProductionName + "': ", production);
        }

        /// <summary>
        /// Returns a string of fact ID information for tracing purposes.
        /// </summary>
        /// <param name="initMsg">Initial string message.</param>
        /// <param name="production">A production containing a list if fact Identifiers.</param>
        [System.Diagnostics.Conditional("DEBUG")]
        private static void TraceFactIds(string initMsg, Tuple production)
        {
            var factIds = string.Empty;

            if (production.Identifiers == null)
            {
                System.Diagnostics.Debug.WriteLine(initMsg + factIds);
                return;
            }

            var ids = production.Identifiers;

            for (var idx = 0; idx < ids.Count; idx++)
            {
                factIds += "f" + ids[idx].ToString(CultureInfo.InvariantCulture) + ", ";
            }

            System.Diagnostics.Debug.WriteLine(initMsg + factIds);
        }

        /// <summary>
        /// Removes a production tuple from the agenda list.
        /// </summary>
        /// <param name="production">The production tuple to be removed from the agenda list.</param>
        private void RemoveProduction(Tuple production)
        {
            var nextProd = production.AgendaNext;
            var prevProd = production.AgendaPrevious;

            if (nextProd == null)
            {
                if (prevProd == null)
                {
                    this.tail = null;
                    this.head = null;
                }
                else
                {
                    prevProd.AgendaNext = null;
                    this.tail = prevProd;
                }
            }
            else
            {
                if (prevProd == null)
                {
                    nextProd.AgendaPrevious = null;
                    this.head = nextProd;
                }
                else
                {
                    nextProd.AgendaPrevious = prevProd;
                    prevProd.AgendaNext = nextProd;
                }
            }

            this.count--;
        }

        #endregion
    }
}
